import axios from 'axios';
import type {
  AxiosInstance,
  AxiosRequestConfig,
  InternalAxiosRequestConfig,
  AxiosResponse,
  AxiosError
} from 'axios';
import { ElMessage } from 'element-plus';
import { getToken, setToken } from '@/utils/token';
import type { Token } from '@/utils/token';
import router from '@/router';
import { useLocaleStore } from '@/stores/modules/locale';

// ABP错误信息数据格式{2}
interface AbpError {
  error: AbpErrorItemData;
}
interface AbpErrorItemData {
  code?: string;
  message: string;
  details: string;
  data: Record<string, any>;
  validationErrors: Array<{
    message: string;
    members: Array<string>;
  }>;
}

// ABP错误信息数据格式{1}
interface AbpErrorData {
  traceId?: string;
  type?: string;
  title?: string;
  status: number;
  errors: Record<string, string>;
}

// 刷新Token标记
interface RefreshTokenFlag {
  // 是否正在刷新
  isRefreshing: boolean;
  //重试队列
  requests: any;
}

// 接口拦截
interface InterceptorHooks {
  requestInterceptor?: (config: InternalAxiosRequestConfig) => InternalAxiosRequestConfig;
  requestInterceptorCatch?: (error: any) => any;
  responseInterceptor?: (response: AxiosResponse) => AxiosResponse;
  responseInterceptorCatch?: (
    error: any,
    instance: AxiosInstance,
    refreshToken: RefreshTokenFlag,
    frequency?: number
  ) => any;
}

// 拦截配置
interface HRequestConfig extends AxiosRequestConfig {
  interceptorHooks?: InterceptorHooks;
}

// 刷新token请求
interface RefreshTokenRequest {
  refreshToken: string;
}

// HttpRequest
class HttpRequest {
  // axios 实例
  instance: AxiosInstance;
  // 基础配置，url和超时时间
  baseConfig: AxiosRequestConfig = { baseURL: '/api', timeout: 60000 };

  refreshToken: RefreshTokenFlag;

  // 构造函数
  constructor(config: HRequestConfig) {
    // 创建axios 实例
    this.instance = axios.create(Object.assign(this.baseConfig, config));

    // 初始化是否正在刷新
    this.refreshToken = { isRefreshing: false, requests: [] };

    this.setupInterceptor(config.interceptorHooks);
  }

  // 设置拦截器
  setupInterceptor(interceptorHooks?: InterceptorHooks): void {
    if (interceptorHooks) {
      this.instance.interceptors.request.use(
        interceptorHooks?.requestInterceptor!,
        interceptorHooks?.requestInterceptorCatch
      );

      this.instance.interceptors.response.use(
        interceptorHooks?.responseInterceptor,
        async (axiosError: AxiosError) => {
          if (interceptorHooks?.responseInterceptorCatch) {
            return interceptorHooks?.responseInterceptorCatch(
              axiosError,
              this.instance,
              this.refreshToken
            );
          }
          return Promise.reject(axiosError.response);
        }
      );
    }
  }

  // request
  public request(config: AxiosRequestConfig): Promise<AxiosResponse> {
    return this.instance.request(config);
  }

  // getUri
  public getUri(config?: AxiosRequestConfig): string {
    return this.instance.getUri(config);
  }

  // get
  public async get<T, D = any>(url: string, config?: AxiosRequestConfig<D>): Promise<T> {
    return await this.instance.get(url, config);
  }

  // post (T:返回参数类型 D:传入参数类型)
  public post<T, D = any>(url: string, data?: D, config?: AxiosRequestConfig<D>): Promise<T> {
    return this.instance.post(url, data, config);
  }

  // put
  public put<T, D = any>(url: string, data?: D, config?: AxiosRequestConfig<D>): Promise<T> {
    return this.instance.put(url, data, config);
  }

  // delete
  public delete<T, D = any>(url: string, config?: AxiosRequestConfig<D>): Promise<T> {
    return this.instance.delete(url, config);
  }

  // head
  public head<T, D = any>(url: string, config?: AxiosRequestConfig<D>): Promise<T> {
    return this.instance.head(url, config);
  }

  // options
  public options<T, D = any>(url: string, config?: AxiosRequestConfig<D>): Promise<T> {
    return this.instance.options(url, config);
  }

  // patch
  public patch<T, D = any>(url: string, data?: D, config?: AxiosRequestConfig<D>): Promise<T> {
    return this.instance.patch(url, data, config);
  }

  // postForm
  public postForm<T, D = any>(url: string, data?: D, config?: AxiosRequestConfig<D>): Promise<T> {
    return this.instance.postForm(url, data, config);
  }

  // putForm
  public putForm<T, D = any>(url: string, data?: D, config?: AxiosRequestConfig<D>): Promise<T> {
    return this.instance.putForm(url, data, config);
  }

  // patchForm
  public patchForm<T, D = any>(url: string, data?: D, config?: AxiosRequestConfig<D>): Promise<T> {
    return this.instance.patchForm(url, data, config);
  }
}

// 默认HttpReques
const defaultHttpRequest: HttpRequest = new HttpRequest({
  baseURL: import.meta.env.VITE_APP_BASE_API,
  interceptorHooks: {
    requestInterceptor: (config: InternalAxiosRequestConfig) => {
      // 一般会请求拦截里面加token，用于后端的验证
      setAuthorization(config);

      // 设置请求语言
      setLanguage(config);

      return config;
    },
    requestInterceptorCatch: (err: any) => {
      // 请求错误，这里可以用全局提示框进行提示
      return Promise.reject(err);
    },
    responseInterceptor: (res: AxiosResponse) => {
      // 直接返回res，当然你也可以只返回res.data
      // 系统如果有自定义code也可以在这里处理
      return res.data;
    },
    async responseInterceptorCatch(
      axiosError: any,
      instance: AxiosInstance,
      refreshTokenFlag: RefreshTokenFlag,
      frequency?: number
    ) {
      return await handleResponseInterceptorCatch(axiosError, instance, refreshTokenFlag, frequency);
    }
  }
});

// 上传文件HttpReques
// @ts-ignore: Deprecated since version 3.0. Use 'httpRequest' instead.
export const uplaodHttpRequest: HttpRequest = new HttpRequest({
  baseURL: import.meta.env.VITE_APP_BASE_API,
  headers: {
    'Content-Type': 'multipart/form-data'
  },
  interceptorHooks: {
    requestInterceptor: (config: InternalAxiosRequestConfig) => {
      // 一般会请求拦截里面加token，用于后端的验证
      setAuthorization(config);

      // 设置请求语言
      setLanguage(config);

      return config;
    },
    requestInterceptorCatch: (err: any) => {
      // 请求错误，这里可以用全局提示框进行提示
      return Promise.reject(err);
    },
    responseInterceptor: (res: AxiosResponse) => {
      // 直接返回res，当然你也可以只返回res.data
      // 系统如果有自定义code也可以在这里处理
      return res.data;
    },
    async responseInterceptorCatch(
      axiosError: any,
      instance: AxiosInstance,
      refreshTokenFlag: RefreshTokenFlag
    ) {
      return await handleResponseInterceptorCatch(axiosError, instance, refreshTokenFlag);
    }
  }
});

// 登录HttpReques
// @ts-ignore: Deprecated since version 3.0. Use 'httpRequest' instead.
export const loginHttpRequest: HttpRequest = new HttpRequest({
  baseURL: import.meta.env.VITE_APP_BASE_API,
  interceptorHooks: {
    requestInterceptor: (config: InternalAxiosRequestConfig) => {
      // 设置请求语言
      setLanguage(config);
      return config;
    },
    requestInterceptorCatch: (err: any) => {
      // 请求错误，这里可以用全局提示框进行提示
      return Promise.reject(err);
    },
    responseInterceptor: (res: AxiosResponse) => {
      // 直接返回res，当然你也可以只返回res.data
      // 系统如果有自定义code也可以在这里处理
      return res.data;
    },
    responseInterceptorCatch: (axiosError: any) => {
      let message: string;
      // 这里用来处理http常见错误，进行全局提示
      if (axiosError.response === undefined) {
        message = '请检查网络或联系管理员';
      } else {
        message = getMessage(axiosError);
      }

      // 这里错误消息可以使用全局弹框展示出来
      // 比如element plus 可以使用 ElMessage
      ElMessage({
        showClose: true,
        message: `${message}`,
        type: 'error'
      });
      // 这里是AxiosError类型，所以一般我们只reject我们需要的响应即可
      return Promise.reject(axiosError.response);
    }
  }
});

// 工作流HttpReques
// @ts-ignore: Deprecated since version 3.0. Use 'httpRequest' instead.
export const workflowHttpRequest: HttpRequest = new HttpRequest({
  baseURL: import.meta.env.VITE_APP_WORKFLOW_API,
  interceptorHooks: {
    requestInterceptor: (config: InternalAxiosRequestConfig) => {
      // 一般会请求拦截里面加token，用于后端的验证
      setAuthorization(config);

      // 设置请求语言
      setLanguage(config);

      return config;
    },
    requestInterceptorCatch: (err: any) => {
      // 请求错误，这里可以用全局提示框进行提示
      return Promise.reject(err);
    },
    responseInterceptor: (res: AxiosResponse) => {
      // 直接返回res，当然你也可以只返回res.data
      // 系统如果有自定义code也可以在这里处理
      return res.data;
    },
    async responseInterceptorCatch(
      axiosError: any,
      instance: AxiosInstance,
      refreshTokenFlag: RefreshTokenFlag
    ) {
      return await handleResponseInterceptorCatch(axiosError, instance, refreshTokenFlag);
    }
  }
});

function setLanguage(config: InternalAxiosRequestConfig) {
  const localeStore = useLocaleStore();
  if (localeStore?.currentLocale?.lang) {
    config.headers!.set('Accept-Language', localeStore?.currentLocale?.lang);
  }
}

function setAuthorization(config: InternalAxiosRequestConfig) {
  const token: Token | null = getToken();
  if (token != null && token.accessToken != '') {
    config.headers!.Authorization = `Bearer ${token.accessToken}`;
  }
}

// {
//   "error": {
//       "code": null,
//       "message": "您的请求无效！",
//       "details": "验证过程中检测到以下错误。\r\n - 字典Key输入错误 长度1-20位\r\n",
//       "data": {},
//       "validationErrors": [
//           {
//               "message": "字典Key输入错误 长度1-20位",
//               "members": [
//                   "key"
//               ]
//           }
//       ]
//   }
// }

// {
//   "type": "https://tools.ietf.org/html/rfc9110#section-15.5.1",
//   "title": "One or more validation errors occurred.",
//   "status": 400,
//   "errors": {
//       "Email": [
//           "字段Email不是有效的邮箱地址."
//       ]
//   },
//   "traceId": "00-4eb7af9145fba95ea76a71756a5af6b0-b204a25c9740b6f0-00"
// }

// 类型缩小
function isAbpErrorData(data: AbpErrorData | AbpError): data is AbpErrorData {
  return 'errors' in data && typeof data.errors === 'object';
}

// 类型缩小
function isAbpError(data: AbpErrorData | AbpError): data is AbpError {
  return 'error' in data && data.error !== undefined;
}

function getMessage(axiosError: AxiosError<AbpErrorData | AbpError>) {
  let message = '连接出错';
  switch (axiosError.response?.status) {
    case 400: {
      const data = axiosError.response?.data;
      if (data) {
        if (isAbpError(data)) {
          const error = data?.error;
          if (error) {
            const validationErrors = error.validationErrors;
            if (validationErrors) {
              message = validationErrors[0].message;
            } else {
              message = error.details ?? error.message;
            }
          }
        } else if (isAbpErrorData(data)) {
          const errors = data?.errors;
          message = errors?.[Object.keys(errors)[0]][0];
        }
      }
      message = message ?? '请求错误(400)';
      break;
    }
    case 401:
      {
        const msg = getHttpResponseError(axiosError);
        message = msg ?? '未授权，请重新登录(401)';
      }
      break;
    case 403:
      {
        const msg = getHttpResponseError(axiosError);
        message = msg ?? '拒绝访问(403)';
      }
      break;
    case 404:
      message = '请求出错(404)';
      break;
    case 408:
      message = '请求超时(408)';
      break;
    case 500:
      message = '服务器错误(500)';
      break;
    case 501:
      message = '服务未实现(501)';
      break;
    case 502:
      message = '网络错误(502)';
      break;
    case 503:
      message = '服务不可用(503)';
      break;
    case 504:
      message = '网络超时(504)';
      break;
    case 505:
      message = 'HTTP版本不受支持(505)';
      break;
    default:
      message = `连接出错(${axiosError.response?.status})!`;
  }
  return message;
}

function getHttpResponseError(axiosError: AxiosError) {
  const data: any = axiosError.response?.data;
  return data?.error?.message;
}

let refreshFrequency = 0;
// 处理错误信息
async function handleResponseInterceptorCatch(
  axiosError: any,
  instance: AxiosInstance,
  refreshTokenFlag: RefreshTokenFlag,
  frequency: number = 200
) {
  let message: string = '连接错误';
  // 这里用来处理http常见错误，进行全局提示
  if (axiosError.response === undefined) {
    message = '请检查网络或联系管理员';
  } else if (axiosError.response?.status === 401) {
    // 1.根据刷新refreshToken刷新accessToken
    // 2.刷新成功 则存将新的refreshToken存入localStorage 并且重新请求当前接口
    // 3.刷新失败 则跳转登录页面重新登录

    if (refreshTokenFlag.isRefreshing === true) {
      // 返回未执行 resolve 的 Promise
      return new Promise((resolve) => {
        // 用函数形式将 resolve 存入，等待刷新后再执行
        refreshTokenFlag.requests.push((token: string) => {
          axiosError.config!.headers.Authorization = `Bearer ${token}`;
          resolve(instance.request(axiosError.config!));
        });
      });
    }

    if (refreshFrequency > frequency) {
      router.push('/login');
      return Promise.reject('不在刷新');
    }

    // 刷新接口
    const token: Token | null = getToken();
    if (token === null) {
      router.push('/login');
      return Promise.reject('跳转到登录');
    }

    try {
      refreshTokenFlag.isRefreshing = true;
      const tokenResult = await refreshToken({ refreshToken: token.refreshToken });
      setToken(tokenResult);
      axiosError.config!.headers.Authorization = `Bearer ${tokenResult.accessToken}`;

      // token 刷新后将数组的方法重新执行
      refreshTokenFlag.requests.forEach((cb: any) => cb(token));

      refreshFrequency++;

      // 重新请求
      return instance.request(axiosError.config!);
    } catch (error: any) {
      // 这里是AxiosError类型，所以一般我们只reject我们需要的响应即可
      return Promise.reject(axiosError.response);
    } finally {
      refreshTokenFlag.isRefreshing = false;
    }
  } else {
    message = getMessage(axiosError);
  }

  // 这里错误消息可以使用全局弹框展示出来
  // 比如element plus 可以使用 ElMessage
  ElMessage({
    showClose: true,
    message: `${message}`,
    type: 'error'
  });
  // 这里是AxiosError类型，所以一般我们只reject我们需要的响应即可
  return Promise.reject(axiosError.response);
}

//刷新
const refreshToken = (data: RefreshTokenRequest) =>
  loginHttpRequest.post<Token>('/api/basics/accounts/refresh', data);

// httpRequestBase
const httpRequestBase = (baseUrl: string): HttpRequest => {
  return new HttpRequest({
    baseURL: baseUrl,
    interceptorHooks: {
      requestInterceptor: (config: InternalAxiosRequestConfig) => {
        // 一般会请求拦截里面加token，用于后端的验证
        setAuthorization(config);

        // 设置请求语言
        setLanguage(config);

        return config;
      },
      requestInterceptorCatch: (err: any) => {
        // 请求错误，这里可以用全局提示框进行提示
        return Promise.reject(err);
      },
      responseInterceptor: (res: AxiosResponse) => {
        // 直接返回res，当然你也可以只返回res.data
        // 系统如果有自定义code也可以在这里处理
        return res.data;
      },
      async responseInterceptorCatch(
        axiosError: any,
        instance: AxiosInstance,
        refreshTokenFlag: RefreshTokenFlag
      ) {
        return await handleResponseInterceptorCatch(axiosError, instance, refreshTokenFlag);
      }
    }
  });
};

// httpRequest
const httpRequest = (config: HRequestConfig): HttpRequest => {
  return new HttpRequest(config);
};

// 工作流HttpReques
const workflowHttpRequestFun = () => httpRequestBase(import.meta.env.VITE_APP_WORKFLOW_API);
// 字典HttpReques
const dictttpRequestFun = () => httpRequestBase(import.meta.env.VITE_APP_DICT_API);

// 导出
export {
  httpRequestBase,
  httpRequest,
  workflowHttpRequestFun,
  dictttpRequestFun
};
export default defaultHttpRequest;
